package com.laolu.icon.remote;

import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.BaseTSD.SIZE_T;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinNT.*;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import static com.sun.jna.platform.win32.WinNT.*;

@Slf4j
@Getter
public abstract class RemoteMemoryOperation implements AutoCloseable {
	/**
	 * 对象内存大小
	 */
	private final SIZE_T size;
	
	/**
	 * 远程内存地址
	 */
	private final Pointer remoteAddress;
	
	/**
	 * 进程句柄
	 */
	private final HANDLE hProcess;
	
	/**
	 * 构造函数
	 * @param size
	 * 		要远程创建的内存大小
	 * @param hProcess
	 * 		进程句柄
	 */
	protected RemoteMemoryOperation(int size, @NotNull HANDLE hProcess) {
		this.size = new SIZE_T(size);
		this.hProcess = hProcess;
		this.remoteAddress = Kernel32.INSTANCE.VirtualAllocEx
				(hProcess, null, this.size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
		log.info("远程申请内存,进程句柄:{},远程内存地址:{},地址大小:{}", hProcess, remoteAddress, this.size);
	}
	
	/**
	 * 写入远程进程内存
	 * @return 是否写入成功
	 */
	public abstract boolean write();
	
	/**
	 * 写入远程进程内存
	 * @param pointer
	 * 		要写入到远程内存中的本地内存数据指针
	 * @return 是否写入成功
	 */
	protected boolean write(Pointer pointer) {
		return Kernel32.INSTANCE.WriteProcessMemory
				(hProcess, remoteAddress, pointer, size.intValue(), null);
	}
	
	/**
	 * 读入远程进程内存
	 * @return
	 */
	public abstract boolean read();
	
	/**
	 * 读取远程进程内存
	 * @param pointer
	 * 		要读取远程内存到本地内存数据指针
	 * @return 是否读取成功
	 */
	protected boolean read(Pointer pointer) {
		return Kernel32.INSTANCE.ReadProcessMemory
				(hProcess, remoteAddress, pointer, size.intValue(), null);
	}
	
	/**
	 * 判断是否成功
	 * @return 是否成功
	 */
	public boolean isOk() {
		return remoteAddress != null;
	}
	
	@Override
	public void close() throws Exception {
		if (remoteAddress != null) {
			if (Kernel32.INSTANCE.VirtualFreeEx(hProcess, remoteAddress, new SIZE_T(0), MEM_RELEASE)) {
				log.info("远程释放内存 远程内存地址:{},进程句柄:{}", remoteAddress, hProcess);
			} else {
				log.error("远程释放内存失败,错误代码:{}", Kernel32.INSTANCE.GetLastError());
			}
		}
	}
}
